package com.btm.qiaoqiang.ruleadp.parser;

import com.btm.qiaoqiang.ruleadp.CustomDynamicComparator;
import lombok.Data;
import org.springframework.util.CollectionUtils;

import java.util.Objects;

/**
 * 规则匹配检测
 *
 * @author: btm
 * @time: 14:19 2020/9/15
 **/
@Data
public class RuleExpression {

    /**根节点指针**/
    private RuleNode rootNode;
    /**当前操作节点指针**/
    private RuleNode currentNode;
    /**临时节点指针**/
    private RuleNode tempNode = null;
    /**解析规则正文的当前字符下标**/
    private int charIndex = 0;
    private final CustomDynamicComparator customDynamicComparator;

    public RuleExpression(CustomDynamicComparator customDynamicComparator) {
        this.rootNode = new RuleNode(this.charIndex);
        this.currentNode = this.rootNode;
        this.customDynamicComparator = customDynamicComparator;
    }

    /**
     * 手动设置正在处理的规则正文字符下标
     * @param charIndex
     */
    public void setNextChar(int charIndex) {
        this.charIndex = charIndex;
    }

    /**正在处理的规则正文字符下标后移一位**/
    public void nextChar() {
        this.charIndex++;
    }

    /**
     * 检测给定对象是否符合当前规则树的任意规则，没有任何规则节点则直接返回true，即没有规则则所有数据都符合
     * @param object 给定的待检测对象
     * @return true:能匹配到一条规则；false：未匹配到任何一条规则
     */
    public boolean check(Object object) {
        //每次检测都从根节点出发，所以将当前节点指针重置到根节点
        currentToRootNode();
        //没有任何规则节点则直接返回true，即没有规则则所有数据都符合
        if (CollectionUtils.isEmpty(this.currentNode.getChildNodeList())) {
            return true;
        }
        ValueCheck valueCheck = new ValueCheck(this.customDynamicComparator);
        for (RuleNode ruleNode : this.currentNode.getChildNodeList()) {
            if (valueCheck.check(ruleNode, object)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 绘制解析到的规则树
     * @return 规则树
     */
    public String generateRuleTree() {
        String title = "\r\n以下为规则表达式解析后的规则项，每一行为一个匹配规则，每一行中的的每个规则项之间为and关系：\r\n";
        StringBuilder stringBuilder = new StringBuilder(title);
        this.rootNode.drawRuleTree("",stringBuilder);
        stringBuilder.append("\r\n");
        return stringBuilder.toString();
    }

    /**
     * 将当前节点指针指向当前节点的父节点
     */
    public void currentToSupperNode() {
        if (this.currentNode != this.rootNode) {
            this.currentNode = this.currentNode.getSupperNode();
        }
    }

    public void rememberCurrentNo() {
        this.tempNode = this.currentNode;
    }

    public void currentToTempNode() {
        this.currentNode = this.tempNode;
        this.tempNode = null;
    }

    public void currentToRootNode() {
        this.currentNode = this.rootNode;
    }

    /**
     * 新建一个空的子节点，并移动当前节点指针指向新建的节点
     */
    public void createNullChildNode(String fieldName) {
        RuleNode ruleNode = new RuleNode(this.charIndex);
        ruleNode.setFieldName(fieldName);
        ruleNode.setMethodName("get" + String.valueOf(fieldName.charAt(0)).toUpperCase() + fieldName.substring(1));
        ruleNode = this.currentNode.addChildNode(ruleNode,this.currentNode.isVirtualNode());
        this.currentNode = ruleNode;
    }

    /**
     * 拼接节点
     * @param ruleExpression 要被拼接上的节点
     */
    public void splicingRuleExpression(RuleExpression ruleExpression) {
        if (Objects.isNull(ruleExpression)) {
            throw new RuntimeException("expression error, get a '}' but not found '{' before");
        }
        RuleNode splicingRootNode = ruleExpression.getRootNode();
        splicingRootNode.virtualization();
        splicingRootNode = this.currentNode.addChildNode(splicingRootNode,this.currentNode.isVirtualNode());
        this.charIndex = ruleExpression.charIndex;
        if (!this.currentNode.isVirtualNode()) {
            this.currentNode = splicingRootNode;
        }
    }

    /**
     * 清理虚拟节点
     */
    public void cleanVirtualNode() {
        this.rootNode.cleanVirtualNode();
    }

    public void setCurrentNodeValue(String value, boolean isCustomDynamicValue) {
        this.currentNode.setValue(value, this.currentNode.isVirtualNode());
        this.currentNode.setCustomDynamicValue(isCustomDynamicValue);
    }

    public void setCurrentNodeValue(boolean flag) {
        this.currentNode.setValue(flag, this.currentNode.isVirtualNode());
    }

    public void setCurrentNodeEqual() {
        this.currentNode.setEqual(true, this.currentNode.isVirtualNode());
    }

    public void setCurrentNodeGt() {
        this.currentNode.setGt(true, this.currentNode.isVirtualNode());
    }

    public void setCurrentNodeGte() {
        this.currentNode.setGte(true, this.currentNode.isVirtualNode());
    }

    public void setCurrentNodeLt() {
        this.currentNode.setLt(true, this.currentNode.isVirtualNode());
    }

    public void setCurrentNodeLte() {
        this.currentNode.setLte(true, this.currentNode.isVirtualNode());
    }

    public void setCurrentNodeNotEqual() {
        this.currentNode.setNotEqual(true, this.currentNode.isVirtualNode());
    }

    public boolean currentNodeIsEqual() {
        return this.currentNode.isEqual();
    }

    public boolean currentNodeIsGt() {
        return this.currentNode.isGt();
    }

    public boolean currentNodeIsGte() {
        return this.currentNode.isGte();
    }

    public boolean currentNodeIsLt() {
        return this.currentNode.isLt();
    }

    public boolean currentNodeIsLte() {
        return this.currentNode.isLte();
    }
}
